home *** CD-ROM | disk | FTP | other *** search
- #!/usr/bin/perl
- #
- # $Id: dns2hosts,v 8.1 1996/10/25 04:57:11 vixie Exp $
- #
- # Convert DNS master (RFC-1035) files to /etc/hosts format.
- # Marion Hakanson (hakanson@cse.ogi.edu)
- # Oregon Graduate Institute of Science and Technology
- #
- # Copyright (c) 1990, Marion Hakanson.
- #
- # You may distribute under the terms of the GNU General Public License
- # as specified in the README file that comes with the dnsparse kit.
- #
- # Other than "-d defdom" (to specify a default domain different from that
- # found in /etc/resolv.conf), command line args are DNS master file names,
- # as described for the dns_init() subroutine in dnsparse.pl. The output
- # is printed to the standard output stream.
- #
- # This Perl program looks at the A and CNAME records encountered in the
- # DNS master files, and produces an /etc/hosts file which includes the
- # same host-to-address, address-to-host, and alias-to-host mappings.
- # Note that some such mappings are necessarily dependent on the order in
- # which they are encountered in the DNS master files. The DNS system
- # is not sensitive to the order, but /etc/hosts files are, thus one
- # should take care that the command line arguments are presented in
- # the same order from one invocation to the next, if one expects the
- # two resulting output files to be comparable.
- #
- # A large part of the complication of this program is due to the notion
- # of the "default domain" that the BIND implementation of a DNS resolver
- # provides. Namely, this allows a user to type a short alias and have
- # it match to a fully qualified domain name (FQDN) somewhere in the
- # machine's default domain. E.g. given "admin" and a default domain
- # of "cse.ogi.edu", the resolver will try "admin.cse.ogi.edu" -- if no
- # match is found, it will try "admin.ogi.edu" (but it will not try
- # just the first-level domain, "admin.edu").
- #
- # Thus, the aliases and canonical names are set up in the output file
- # to mimic the behavior of a system using the BIND resolver. Among
- # other things, this means that host numbers will always be resolved
- # to the FQDN of the host. Instead of using PTR records to provide
- # the number-to-name mappings, this program assumes that the PTR records
- # would be automatically generated from the A records. It further assumes
- # that a host is considered to have a single, unique FQDN, and that all
- # of its addresses (interfaces) will resolve back to that FQDN. To my
- # knowledge, this is the only area where the output hosts file may not
- # quite mimic the behavior of the BIND resolver. I would be willing to
- # consider a design change if demand warrants it.
-
- do 'getopts.pl'; die "$@, aborted" if $@;
- do 'dnsparse.pl'; die "$@, aborted" if $@;
-
- do Getopts('d:');
-
- # Get default domain
- if ( defined($opt_d) ) {
- $defdom = $opt_d;
- } elsif ( open(F, '</etc/resolv.conf') ) {
- while ( <F> ) {
- $defdom = $1 if ( /\s*domain\s+([^\s]+)/ );
- }
- close(F);
- }
-
- # Set up domains the resolver would try
- if ( defined($defdom) ) {
- $defdom =~ s/^\.//; # strip leading dot
- $defdom = do dns_makefqdn($defdom,'');
-
- @domparts = split(/\./,$defdom); # escaped dots?
- while ( $#domparts > 0 ) {
- push(@defdoms, join('.',@domparts));
- shift(@domparts);
- }
- }
-
- # Parse the dns db file, collecting names & addrs.
- do dns_init(@ARGV);
-
- open(OFILE, ">&STDOUT") || die "Cannot dupe 'STDOUT', aborted";
-
- # Treat loopback address specially, since the DNS is usually set
- # up to resolve loopback to "localhost".
- $hostsbyaddr{'127.0.0.1'} = 'localhost';
- push(@addrs,'127.0.0.1');
-
- rr: while ( (@rr = do dns_getrr()) && @rr ) {
- ($domain, $ttl, $class, $type, @data) = @rr;
-
- next rr if ( $class ne 'IN' );
-
- case: {
- if ( $type eq 'A' ) {
- $list = '';
- if ( defined($hostsbyaddr{$data[0]}) ) {
- $list = $hostsbyaddr{$data[0]} . $dns'delim;
- } else {
- push(@addrs,$data[0]);
- }
- $list .= $domain;
- $hostsbyaddr{$data[0]} = $list;
- $fqdnseen{$domain}++;
- last case;
- } elsif ( $type eq 'CNAME' ) {
- $list = '';
- if ( defined($cnamesbyhost{$data[0]}) ) {
- $list = $cnamesbyhost{$data[0]} . $dns'delim;
- }
- $list .= $domain;
- $cnamesbyhost{$data[0]} = $list;
- $fqdnseen{$domain}++;
- last case;
- }
- }
- }
-
- # Go through the names encountered, building abbreviations,
- # and attaching them to their fully-qualified forms.
- while ( ($fqdn,$cnt) = each %fqdnseen ) {
- #print STDERR "$fqdn seen $cnt times\n";
- foreach $truncdom (@defdoms) {
- $abbrev = $fqdn;
- # don't generate abbrev's which wouldn't resolve
- if ( $abbrev =~ s/\.$truncdom// ) {
- # now we mimic what the resolver would do
- foreach $dom (@defdoms) {
- if ( defined($fqdnseen{"$abbrev.$dom"}) ) {
- $abbrevsbyhost{"$abbrev.$dom"} .= $abbrev . $dns'delim;
- #print STDERR "$abbrev -> $abbrev.$dom\n";
- # stop with the first one
- last;
- }
- }
- }
- }
- }
-
- # for debugging
- #while ( ($key,$val) = each(%abbrevsbyhost) ) {
- # print STDERR "|$key";
- # @vals = split(/$dns'delim/,$val);
- # foreach $val (@vals) {
- # print STDERR "|$val";
- # }
- # print STDERR "|\n";
- #}
-
- # Write out the hosts file.
- # First write out some commentary about the input source, etc.
- $datefmt = '%02d/%02d/%02d %02d:%02d:%02d';
- ($sec,$min,$hour,$mday,$mon,$year,@rest) = localtime();
- $mon++; # 0 == January
- $date = sprintf($datefmt, $year, $mon, $mday, $hour, $min, $sec);
-
- chop($host = `hostname`);
- print OFILE "#\n";
- print OFILE "# Host table (/etc/hosts)\n";
- print OFILE "#\n";
- print OFILE "# Created on host '$host' at $date\n";
- print OFILE "#\n";
- print OFILE "# Abbreviations set up for default domain '$defdom'\n";
- print OFILE "#\n";
- print OFILE "# THIS TABLE WAS GENERATED AUTOMATICALLY.\n";
- print OFILE "# ANY CHANGES OR ADDITIONS TO IT WILL BE\n";
- print OFILE "# LOST WHEN THE NEXT UPDATE TAKES PLACE.\n";
- print OFILE "#\n";
- print OFILE "# Built from the following DNS master files:\n";
- print OFILE "#\n";
- print OFILE "# ModTime\t\tFile\n";
- print OFILE "#\tOrigin\n";
-
- while ( $#ARGV >= $[ ) {
- ($file,$origin) = do dns_commasplit(shift(@ARGV));
- $origin = '.' unless ( $origin ); # root
- if ( $file eq '' || $file eq '-' ) {
- $file eq 'Standard Input';
- @ARGV = (); # STDIN is last
- $date = 'See Above';
- } else {
- if ( -r $file ) {
- ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
- $atime,$mtime,$ctime,$blksize,$blocks) = stat(_);
- ($sec,$min,$hour,$mday,$mon,$year,@rest) = localtime($mtime);
- $mon++; # 0 == January
- $date = sprintf($datefmt, $year, $mon, $mday, $hour, $min, $sec);
- } else {
- $date = "Not Found";
- $origin = $!; # the error message
- }
- }
- print OFILE "# $date\t$file\n";
- print OFILE "#\t$origin\n";
- }
- print OFILE "#\n";
- print OFILE "#\n";
-
- # Build up an output record and then write it out using a
- # format which may break a long record into multiple lines,
- # all starting with the same address and canonical name.
- format OFILE =
- @<<<<<<<<<<<<<< ~^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
- $addr, $names
- .
-
- $: = ' '; # OK to break $names on a blank
-
- foreach $addr ( @addrs ) {
- @hosts = split(/$dns'delim/, $hostsbyaddr{$addr});
- %nameseen = ();
- $names = '';
- $canon = $hosts[0]; # save the first one
-
- foreach $host ( @hosts ) {
- unless ( defined($nameseen{$host}) ) {
- $nameseen{$host} = 1;
- $names .= " $host";
- if ( defined($abbrevsbyhost{$host}) ) {
- @abbrevs = split(/$dns'delim/, $abbrevsbyhost{$host});
- foreach $abbrev ( @abbrevs ) {
- unless ( defined($nameseen{$abbrev}) ) {
- $nameseen{$abbrev} = 1;
- $names .= " $abbrev";
- }
- }
- }
- }
-
- if ( defined($cnamesbyhost{$host}) ) {
- @aliases = split(/$dns'delim/, $cnamesbyhost{$host});
- foreach $alias ( @aliases ) {
- unless ( defined($nameseen{$alias}) ) {
- $nameseen{$alias} = 1;
- $names .= " $alias";
- if ( defined($abbrevsbyhost{$alias}) ) {
- @abbrevs = split(/$dns'delim/, $abbrevsbyhost{$alias});
- foreach $abbrev ( @abbrevs ) {
- unless ( defined($nameseen{$abbrev}) ) {
- $nameseen{$abbrev} = 1;
- $names .= " $abbrev";
- }
- }
- }
- }
- }
- }
- }
- $names =~ s/^ $canon *//; # we'll put it back below
- do {
- #print STDERR "|$names|\n";
- $names = "$canon $names";
- write(OFILE); # has side effect of shortening $names
- } while ( $names );
- }
-
- exit(0);
-